/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <vmm.h>
#include <arch/sysregs.h>
#include <interrupts.h>

/****************************************************************************
 ****                           公共函数定义                             ****
 ****************************************************************************/
/**
 * 功能描述: VMM与硬件相关的初始化
 *           1. 设置VM的IPA可寻址范围
 *           2. 设置虚拟化行为
 * 入口参数: void
 * 出口参数: void
 * 返 回 值: void
 * 修订历史：
 *      1.  将PT_LVLS宏修改为vm_pt_dscr->lvls（2023-06-01 Tupelo Shen）
 */
void vmm_arch_init(void)
{
    /**
     * Check available physical address range which will limit
     * IPA size. Patch 2-stage page table descriptors if this forces
     * the initial lookup to level 1.
     *
     * In multi-cluster heterogenous we only support the minimum parange 
     * for a vm's physicall adress space.
     * TODO: we could make this more dynamic and adapt it to each virtual 
     * machine.
     */

    static size_t min_parange = 0b111;
    static spinlock_t lock = SPINLOCK_INITVAL;

    /* 获取物理地址可寻址范围 */
    size_t temp_parange = MRS(ID_AA64MMFR0_EL1) & ID_AA64MMFR0_PAR_MSK;
    spin_lock(&lock);
    if(temp_parange < min_parange) {
        min_parange = temp_parange;
    }
    spin_unlock(&lock);

    cpu_sync_barrier(&cpu_glb_sync);

    /* 如果可寻址物理范围小于44位，则VM的页表遍历从Level-1开始 */
    if (cpu.id == CPU_MASTER) {
        parange = min_parange;
        if (parange_table[parange] < 44) {
            for (size_t i = 0; i < vm_pt_dscr->lvls - 1; i++) {
                vm_pt_dscr->lvl_wdt[i] = vm_pt_dscr->lvl_wdt[i + 1];
                vm_pt_dscr->lvl_off[i] = vm_pt_dscr->lvl_off[i + 1];
                vm_pt_dscr->lvl_term[i] = vm_pt_dscr->lvl_term[i + 1];
            }
            vm_pt_dscr->lvl_wdt[0] = parange_table[parange];
            vm_pt_dscr->lvls = vm_pt_dscr->lvls - 1;
        }
    }

    cpu_sync_barrier(&cpu_glb_sync);

    /**
     * 设置VM地址转换的stage-2阶段的行为:
     * 1. VTCR_PS_OFF,          设置stage-2阶段的物理地址大小
     * 2. VTCR_TG0_4K,          设置stage-2阶段的颗粒度大小为4K
     * 3. VTCR_ORGN0_WB_RA_WA,  设置外部缓存属性
     * 4. VTCR_IRGN0_WB_RA_WA,  设置内部缓存属性
     * 5. VTCR_T0SZ,            设置可寻址位数
     * 6. VTCR_SH0_IS,          设置内存的共享属性
     * 7. VTCR_SL0_*,           设置页表遍历从哪一级开始
     */
    uint64_t vtcr = VTCR_RES1 | ((parange << VTCR_PS_OFF) & VTCR_PS_MSK) |
                    VTCR_TG0_4K | VTCR_ORGN0_WB_RA_WA | VTCR_IRGN0_WB_RA_WA |
                    VTCR_T0SZ(64 - parange_table[parange]) | VTCR_SH0_IS |
                    ((parange_table[parange] < 44) ? VTCR_SL0_12 : VTCR_SL0_01);

    MSR(VTCR_EL2, vtcr);

    /**
     * 设置虚拟化行为:
     * 1. HCR_VM_BIT,   使能虚拟化
     * 2. HCR_RW_BIT,   控制EL1的执行状态是`aarch32 or aarch64`
     * 3. HCR_IMO_BIT,  控制物理IRQ是否被EL2捕获
     * 4. HCR_FMO_BIT,  控制物理FIQ是否被EL2捕获
     * 5. HCR_TSC_BIT,  控制SMC指令是否被EL2捕获
     */
    uint64_t hcr = HCR_VM_BIT | HCR_RW_BIT | HCR_IMO_BIT | HCR_FMO_BIT |
                   HCR_TSC_BIT;

    MSR(HCR_EL2, hcr);
}